{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "FM(Factorization Machine)系列主要参考美团的[《深入FFM原理与实践》](https://tech.meituan.com/2016/03/03/deep-understanding-of-ffm-principles-and-practices.html)这篇博客内容做介绍并实现，首先介绍一下原理"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 一.FM原理\n",
    "大家可能用过sklearn中的这个多项式特征处理函数：*`sklearn.preprocessing.PolynomialFeatures`*，它作用是就是将原始特征扩展为多项式特征，比如原始特征为$a,b$，那么它会做如下扩展：   \n",
    "\n",
    "$$\n",
    "[a,b]\\rightarrow [1,a,b,a^2,ab,b^2]\n",
    "$$  \n",
    "\n",
    "而FM的初衷便是对这组新特征做线性建模，一般地，它可以表示如下：   \n",
    "\n",
    "$$\n",
    "y(x)=w_0+\\sum_{i=1}^nw_ix_i+\\sum_{i=1}^{n-1}\\sum_{j=i+1}^nw_{ij}x_ix_j\n",
    "$$  \n",
    "\n",
    "FM通常不会对平方项建模（比如上面的$a^2,b^2$），这里$n$代表样本的特征数量，$x_i$是第$i$个特征值，$w_0,w_i,w_{ij}$是模型参数，到这里大家可能会有疑惑，我们干嘛不先通过多项式特征处理函数做转换，然后再接着做一个线性回归或者logistic回归之类的不就行了吗？那这个...FM拿它何用？如果按照刚刚的操作其实是有很大问题的，主要有两点问题：    \n",
    "\n",
    "（1）**参数爆炸**；   \n",
    "\n",
    "（2）**高维稀疏**；  \n",
    "\n",
    "第一点比较容易理解，对于$n$个特征，$w_{ij}$将会有$\\frac{n(n-1)}{2}$项，形象点说就是平常随意用到的100个特征，扩展后将会有5000个，而参数越多，如果没有足够的数据做训练，模型很容易陷入过拟合，而对于第二点，经常处理离散特征的同学会很容易理解，比如下图\n",
    "![avatar](./source/17_FM_one-hot1.png)  \n",
    "\n",
    "包含有三个特征（最左侧的是标签），且都是离散特征，而对于离散特征我们经常做的一种操作便是one-hot转换，转换后的结果如下图：   \n",
    "\n",
    "![avatar](./source/17_FM_one-hot2.png)  \n",
    "\n",
    "如果我们在对这些特征做多项式转换，可以发现转后的20多个特征中，仅仅只有3个非零特征，这就意味着绝大部分的$x_ix_j$将会是0，而损失函数关于$w_{ij}$的导数必然会包含有$x_ix_j$这一项，这就意味$w_{ij}$大部分情况下就是个摆设，很难被更新到，而FM便可以解决这两个问题，它假设$w_{ij}$由两个向量的内积生成：   \n",
    "\n",
    "$$\n",
    "w_{ij}:=<v_i,v_j>\n",
    "$$  \n",
    "\n",
    "这里，$v_i$表示第$i$个特征的隐向量，其向量长度为$k(k<<n)$，通常$k=4$即可，这时FM模型方程如下：   \n",
    "\n",
    "$$\n",
    "y(x)=w_0+\\sum_{i=1}^nw_ix_i+\\sum_{i=1}^{n-1}\\sum_{j=i+1}^n<v_i,v_j>x_ix_j\n",
    "$$   \n",
    "进一步的，我们可以将其化简为如下形式：   \n",
    "\n",
    "$$\n",
    "y(x)=w_0+\\sum_{i=1}^nw_ix_i+\\frac{1}{2}\\sum_{f=1}^k((\\sum_{i=1}^nv_{i,f}x_i)^2-\\sum_{i=1}^nv_{i,f}^2x_i^2)\n",
    "$$  \n",
    "\n",
    "这里，$v_{i,f}$表示向量$v_i$的第$f$个元素，上述化简用到了这样的关系：$ab+ac+bc=\\frac{1}{2}((a+b+c)^2-(a^2+b^2+c^2))$，接下来我们可以进一步看看梯度：   \n",
    "\n",
    "$$\n",
    "\\frac{\\partial}{\\partial\\theta}y(x)=\\left\\{\\begin{matrix}\n",
    "1 &\\theta=w_0 \\\\ \n",
    "x_i &\\theta=w_i \\\\ \n",
    "x_i\\sum_{j=1}^nv_{j,f}x_j-v_{i,f}x_i^2 & \\theta=v_{i,f}\n",
    "\\end{matrix}\\right.\n",
    "$$  \n",
    "\n",
    "可以发现前面的两个问题可以被FM解决了，第一个问题，参数量从$n(n-1)/2$降低到了$kn$，第二个高维稀疏导致参数无法被训练的问题，对于$v_{i,f}$只要$x_i$不为0，且所有$x_j,j=1,2,...,n$中有一个不为0，那么梯度$\\frac{\\partial}{\\partial v_{i,f}}y(x)$就不为0，这比$x_ix_j$不为0的条件松了很多"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 二.代码实现\n",
    "这里就对FM应用到回归任务做简单实现，更多的功能扩展放到FFM中，下面推导一下损失函数对参数的梯度，假设样本$x$对应的标签为$t$，那么损失函数可以表示如下：   \n",
    "\n",
    "$$\n",
    "L(\\theta)=\\frac{1}{2}(y(x)-t)^2\n",
    "$$   \n",
    "\n",
    "那么：   \n",
    "\n",
    "$$\n",
    "\\frac{\\partial L(\\theta)}{\\partial y(x)}=y(x)-t\n",
    "$$   \n",
    "\n",
    "再根据链式求导有：   \n",
    "\n",
    "$$\n",
    "\\frac{\\partial L(\\theta)}{\\partial \\theta}=\\frac{\\partial L(\\theta)}{\\partial y(x)}\\frac{\\partial y(x)}{\\partial\\theta}\\\\\n",
    "=(y(x)-t)\\cdot \\left\\{\\begin{matrix}\n",
    "1 &\\theta=w_0 \\\\ \n",
    "x_i &\\theta=w_i \\\\ \n",
    "x_i\\sum_{j=1}^nv_{j,f}x_j-v_{i,f}x_i^2 & \\theta=v_{i,f}\n",
    "\\end{matrix}\\right.\n",
    "$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "FM因子分解机的简单实现，只实现了损失函数为平方损失的回归任务，更多功能扩展请使用后续的FFM\n",
    "代码封装到ml_models.fm中\n",
    "\"\"\"\n",
    "import numpy as np\n",
    "\n",
    "\n",
    "class FM(object):\n",
    "    def __init__(self, epochs=1, lr=1e-3, adjust_lr=True, batch_size=1, hidden_dim=4, lamb=1e-3, alpha=1e-3,\n",
    "                 normal=True, solver='adam', rho_1=0.9, rho_2=0.999, early_stopping_rounds=100):\n",
    "        \"\"\"\n",
    "\n",
    "        :param epochs: 迭代轮数\n",
    "        :param lr: 学习率\n",
    "        :param adjust_lr:是否根据特征数量再次调整学习率 max(lr,1/n_feature)\n",
    "        :param batch_size:\n",
    "        :param hidden_dim:隐变量维度\n",
    "        :param lamb:l2正则项系数\n",
    "        :param alpha:l1正则项系数\n",
    "        :param normal:是否归一化，默认用min-max归一化\n",
    "        :param solver:优化方式，包括sgd,adam,默认adam\n",
    "        :param rho_1:adam的rho_1的权重衰减,solver=adam时生效\n",
    "        :param rho_2:adam的rho_2的权重衰减,solver=adam时生效\n",
    "        :param early_stopping_rounds:对early_stopping进行支持，使用rmse作为评估指标，默认20\n",
    "        \"\"\"\n",
    "        self.epochs = epochs\n",
    "        self.lr = lr\n",
    "        self.adjust_lr = adjust_lr\n",
    "        self.batch_size = batch_size\n",
    "        self.hidden_dim = hidden_dim\n",
    "        self.lamb = lamb\n",
    "        self.alpha = alpha\n",
    "        self.solver = solver\n",
    "        self.rho_1 = rho_1\n",
    "        self.rho_2 = rho_2\n",
    "        self.early_stopping_rounds = early_stopping_rounds\n",
    "        # 初始化参数\n",
    "        self.w = None  # w_0,w_i\n",
    "        self.V = None  # v_{i,f}\n",
    "        # 归一化\n",
    "        self.normal = normal\n",
    "        if normal:\n",
    "            self.xmin = None\n",
    "            self.xmax = None\n",
    "\n",
    "    def _y(self, X):\n",
    "        \"\"\"\n",
    "        实现y(x)的功能\n",
    "        :param X:\n",
    "        :return:\n",
    "        \"\"\"\n",
    "        # 去掉第一列bias\n",
    "        X_ = X[:, 1:]\n",
    "        X_V = X_ @ self.V\n",
    "        X_V_2 = X_V * X_V\n",
    "        X_2_V_2 = (X_ * X_) @ (self.V * self.V)\n",
    "        pol = 0.5 * np.sum(X_V_2 - X_2_V_2, axis=1)\n",
    "        return X @ self.w.reshape(-1) + pol\n",
    "\n",
    "    def fit(self, X, y, eval_set=None, show_log=True):\n",
    "        X_o = X.copy()\n",
    "        if self.normal:\n",
    "            self.xmin = X.min(axis=0)\n",
    "            self.xmax = X.max(axis=0)\n",
    "            X = (X - self.xmin) / self.xmax\n",
    "        n_sample, n_feature = X.shape\n",
    "        x_y = np.c_[np.ones(n_sample), X, y]\n",
    "        # 记录loss\n",
    "        train_losses = []\n",
    "        eval_losses = []\n",
    "        # 调整一下学习率\n",
    "        if self.adjust_lr:\n",
    "            self.lr = max(self.lr, 1 / n_feature)\n",
    "        # 初始化参数\n",
    "        self.w = np.random.random((n_feature + 1, 1)) * 1e-3\n",
    "        self.V = np.random.random((n_feature, self.hidden_dim)) * 1e-3\n",
    "        if self.solver == 'adam':\n",
    "            # 缓存梯度一阶，二阶估计\n",
    "            w_1 = np.zeros_like(self.w)\n",
    "            V_1 = np.zeros_like(self.V)\n",
    "            w_2 = np.zeros_like(self.w)\n",
    "            V_2 = np.zeros_like(self.V)\n",
    "        # 更新参数\n",
    "        count = 0\n",
    "        best_eval_value = np.power(2., 1023)\n",
    "        eval_count = 0\n",
    "        for epoch in range(self.epochs):\n",
    "            np.random.shuffle(x_y)\n",
    "            for index in range(x_y.shape[0] // self.batch_size):\n",
    "                count += 1\n",
    "                batch_x_y = x_y[self.batch_size * index:self.batch_size * (index + 1)]\n",
    "                batch_x = batch_x_y[:, :-1]\n",
    "                batch_y = batch_x_y[:, -1:]\n",
    "                # 计算y(x)-t\n",
    "                y_x_t = self._y(batch_x).reshape((-1, 1)) - batch_y\n",
    "                # 更新w\n",
    "                if self.solver == 'sgd':\n",
    "                    self.w = self.w - (self.lr * (np.sum(y_x_t * batch_x, axis=0) / self.batch_size).reshape(\n",
    "                        (-1, 1)) + self.lamb * self.w + self.alpha * np.where(self.w > 0, 1, 0))\n",
    "                elif self.solver == 'adam':\n",
    "                    w_reg = self.lamb * self.w + self.alpha * np.where(self.w > 0, 1, 0)\n",
    "                    w_grad = (np.sum(y_x_t * batch_x, axis=0) / self.batch_size).reshape(\n",
    "                        (-1, 1)) + w_reg\n",
    "                    w_1 = self.rho_1 * w_1 + (1 - self.rho_1) * w_grad\n",
    "                    w_2 = self.rho_2 * w_2 + (1 - self.rho_2) * w_grad * w_grad\n",
    "                    w_1_ = w_1 / (1 - np.power(self.rho_1, count))\n",
    "                    w_2_ = w_2 / (1 - np.power(self.rho_2, count))\n",
    "                    self.w = self.w - (self.lr * w_1_) / (np.sqrt(w_2_) + 1e-8)\n",
    "\n",
    "                # 更新 V\n",
    "                batch_x_ = batch_x[:, 1:]\n",
    "                V_X = batch_x_ @ self.V\n",
    "                X_2 = batch_x_ * batch_x_\n",
    "                # 从i,f单个元素逐步更新有点慢\n",
    "                # for i in range(self.V.shape[0]):\n",
    "                #     for f in range(self.V.shape[1]):\n",
    "                #         if self.solver == \"sgd\":\n",
    "                #             self.V[i, f] -= self.lr * (\n",
    "                #                 np.sum(y_x_t.reshape(-1) * (batch_x_[:, i] * V_X[:, f] - self.V[i, f] * X_2[:, i]))\n",
    "                #                 / self.batch_size + self.lamb * self.V[i, f] + self.alpha * (self.V[i, f] > 0))\n",
    "                #         elif self.solver == \"adam\":\n",
    "                #             v_reg = self.lamb * self.V[i, f] + self.alpha * (self.V[i, f] > 0)\n",
    "                #             v_grad = np.sum(y_x_t.reshape(-1) * (\n",
    "                #                 batch_x_[:, i] * V_X[:, f] - self.V[i, f] * X_2[:, i])) / self.batch_size + v_reg\n",
    "                #             V_1[i, f] = self.rho_1 * V_1[i, f] + (1 - self.rho_1) * v_grad\n",
    "                #             V_2[i, f] = self.rho_2 * V_2[i, f] + (1 - self.rho_2) * v_grad * v_grad\n",
    "                #             v_1_ = V_1[i, f] / (1 - np.power(self.rho_1, count))\n",
    "                #             v_2_ = V_2[i, f] / (1 - np.power(self.rho_2, count))\n",
    "                #             self.V[i, f] = self.V[i, f] - (self.lr * v_1_) / (np.sqrt(v_2_) + 1e-8)\n",
    "\n",
    "                # 从隐变量的维度进行更新\n",
    "                for f in range(self.V.shape[1]):\n",
    "                    if self.solver == 'sgd':\n",
    "                        V_grad = np.sum(\n",
    "                            y_x_t.reshape((-1, 1)) * (batch_x_ * V_X[:, f].reshape((-1, 1)) - X_2 * self.V[:, f]),\n",
    "                            axis=0)\n",
    "                        self.V[:, f] = self.V[:, f] - self.lr * V_grad - self.lamb * self.V[:, f] - self.alpha * (\n",
    "                            self.V[:, f] > 0)\n",
    "                    elif self.solver == 'adam':\n",
    "                        V_reg = self.lamb * self.V[:, f] + self.alpha * (self.V[:, f] > 0)\n",
    "                        V_grad = np.sum(\n",
    "                            y_x_t.reshape((-1, 1)) * (batch_x_ * V_X[:, f].reshape((-1, 1)) - X_2 * self.V[:, f]),\n",
    "                            axis=0) + V_reg\n",
    "                        V_1[:, f] = self.rho_1 * V_1[:, f] + (1 - self.rho_1) * V_grad\n",
    "                        V_2[:, f] = self.rho_2 * V_2[:, f] + (1 - self.rho_2) * V_grad * V_grad\n",
    "                        V_1_ = V_1[:, f] / (1 - np.power(self.rho_1, count))\n",
    "                        V_2_ = V_2[:, f] / (1 - np.power(self.rho_2, count))\n",
    "                        self.V[:, f] = self.V[:, f] - (self.lr * V_1_) / (np.sqrt(V_2_) + 1e-8)\n",
    "\n",
    "                # 计算eval loss\n",
    "                eval_loss = None\n",
    "                if eval_set is not None:\n",
    "                    eval_x, eval_y = eval_set\n",
    "                    eval_loss = np.std(eval_y - self.predict(eval_x))\n",
    "                    eval_losses.append(eval_loss)\n",
    "                # 是否显示\n",
    "                if show_log:\n",
    "                    train_loss = np.std(y - self.predict(X_o))\n",
    "                    print(\"epoch:\", epoch + 1, \"/\", self.epochs, \",samples:\", (index + 1) * self.batch_size, \"/\",\n",
    "                          n_sample,\n",
    "                          \",train loss:\",\n",
    "                          train_loss, \",eval loss:\", eval_loss)\n",
    "                    train_losses.append(train_loss)\n",
    "                # 是否早停\n",
    "                if eval_loss is not None and self.early_stopping_rounds is not None:\n",
    "                    if eval_loss < best_eval_value:\n",
    "                        eval_count = 0\n",
    "                        best_eval_value = eval_loss\n",
    "                    else:\n",
    "                        eval_count += 1\n",
    "                    if eval_count >= self.early_stopping_rounds:\n",
    "                        print(\"---------------early_stopping-----------------------------\")\n",
    "                        break\n",
    "\n",
    "        return train_losses, eval_losses\n",
    "\n",
    "    def predict(self, X):\n",
    "        \"\"\"\n",
    "        :param X:\n",
    "        :return:\n",
    "        \"\"\"\n",
    "        if self.normal:\n",
    "            X = (X - self.xmin) / self.xmax\n",
    "        n_sample, n_feature = X.shape\n",
    "        X_V = X @ self.V\n",
    "        X_V_2 = X_V * X_V\n",
    "        X_2_V_2 = (X * X) @ (self.V * self.V)\n",
    "        pol = 0.5 * np.sum(X_V_2 - X_2_V_2, axis=1)\n",
    "        return np.c_[np.ones(n_sample), X] @ self.w.reshape(-1) + pol"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 三.测试"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "#造伪数据\n",
    "data1 = np.linspace(1, 10, num=200)\n",
    "data2 = np.linspace(1, 10, num=200) + np.random.random(size=200)\n",
    "target = data1 * 2 + data2 * 1 + 10 * data1 * data2 + np.random.random(size=200)\n",
    "data = np.c_[data1, data2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.model_selection import train_test_split\n",
    "X_train, X_test, y_train, y_test = train_test_split(data, target, test_size=0.4, random_state=0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "epoch: 1 / 1 ,samples: 1 / 120 ,train loss: 327.43304536928594 ,eval loss: 291.9721877542085\n",
      "epoch: 1 / 1 ,samples: 2 / 120 ,train loss: 326.7160901918784 ,eval loss: 291.3271189485855\n",
      "epoch: 1 / 1 ,samples: 3 / 120 ,train loss: 325.7151828483661 ,eval loss: 290.43528663690176\n",
      "epoch: 1 / 1 ,samples: 4 / 120 ,train loss: 324.4152130238985 ,eval loss: 289.2810645127806\n",
      "epoch: 1 / 1 ,samples: 5 / 120 ,train loss: 323.01897436328363 ,eval loss: 288.0444933403834\n",
      "epoch: 1 / 1 ,samples: 6 / 120 ,train loss: 321.3838207470574 ,eval loss: 286.5995936954016\n",
      "epoch: 1 / 1 ,samples: 7 / 120 ,train loss: 319.94927792261774 ,eval loss: 285.3310103840077\n",
      "epoch: 1 / 1 ,samples: 8 / 120 ,train loss: 318.5719337115685 ,eval loss: 284.11400223694636\n",
      "epoch: 1 / 1 ,samples: 9 / 120 ,train loss: 316.9476360205337 ,eval loss: 282.6800903373831\n",
      "epoch: 1 / 1 ,samples: 10 / 120 ,train loss: 314.7864859352729 ,eval loss: 280.7747654767163\n",
      "epoch: 1 / 1 ,samples: 11 / 120 ,train loss: 312.13341354273456 ,eval loss: 278.43789101462926\n",
      "epoch: 1 / 1 ,samples: 12 / 120 ,train loss: 309.16581289509793 ,eval loss: 275.8247460891347\n",
      "epoch: 1 / 1 ,samples: 13 / 120 ,train loss: 306.27255717177525 ,eval loss: 273.2781949183259\n",
      "epoch: 1 / 1 ,samples: 14 / 120 ,train loss: 302.69416496771913 ,eval loss: 270.1306586526588\n",
      "epoch: 1 / 1 ,samples: 15 / 120 ,train loss: 298.94444644455086 ,eval loss: 266.8335058027848\n",
      "epoch: 1 / 1 ,samples: 16 / 120 ,train loss: 295.3143220143411 ,eval loss: 263.6423561128022\n",
      "epoch: 1 / 1 ,samples: 17 / 120 ,train loss: 291.3237509064271 ,eval loss: 260.1354974551523\n",
      "epoch: 1 / 1 ,samples: 18 / 120 ,train loss: 287.49921467259674 ,eval loss: 256.77523476633075\n",
      "epoch: 1 / 1 ,samples: 19 / 120 ,train loss: 283.7042670379911 ,eval loss: 253.4414837569022\n",
      "epoch: 1 / 1 ,samples: 20 / 120 ,train loss: 278.9662785039226 ,eval loss: 249.28103217515456\n",
      "epoch: 1 / 1 ,samples: 21 / 120 ,train loss: 274.36280682169763 ,eval loss: 245.23921862961456\n",
      "epoch: 1 / 1 ,samples: 22 / 120 ,train loss: 269.9916328968292 ,eval loss: 241.4018533233914\n",
      "epoch: 1 / 1 ,samples: 23 / 120 ,train loss: 265.6497926902622 ,eval loss: 237.59063822710482\n",
      "epoch: 1 / 1 ,samples: 24 / 120 ,train loss: 261.5304618788339 ,eval loss: 233.97503851467806\n",
      "epoch: 1 / 1 ,samples: 25 / 120 ,train loss: 255.9712297539595 ,eval loss: 229.0974061155879\n",
      "epoch: 1 / 1 ,samples: 26 / 120 ,train loss: 249.63664207715678 ,eval loss: 223.540478679283\n",
      "epoch: 1 / 1 ,samples: 27 / 120 ,train loss: 243.5177699482245 ,eval loss: 218.1731976241854\n",
      "epoch: 1 / 1 ,samples: 28 / 120 ,train loss: 235.93035644162356 ,eval loss: 211.51827263729555\n",
      "epoch: 1 / 1 ,samples: 29 / 120 ,train loss: 228.29334367815406 ,eval loss: 204.8203973571134\n",
      "epoch: 1 / 1 ,samples: 30 / 120 ,train loss: 221.0354471284559 ,eval loss: 198.45549752245287\n",
      "epoch: 1 / 1 ,samples: 31 / 120 ,train loss: 213.95307377288907 ,eval loss: 192.2449043683773\n",
      "epoch: 1 / 1 ,samples: 32 / 120 ,train loss: 207.1184199693237 ,eval loss: 186.25187133472122\n",
      "epoch: 1 / 1 ,samples: 33 / 120 ,train loss: 200.57104162351598 ,eval loss: 180.5110271166571\n",
      "epoch: 1 / 1 ,samples: 34 / 120 ,train loss: 194.30667243270833 ,eval loss: 175.01858282042767\n",
      "epoch: 1 / 1 ,samples: 35 / 120 ,train loss: 185.93707682966206 ,eval loss: 167.68207164353436\n",
      "epoch: 1 / 1 ,samples: 36 / 120 ,train loss: 175.7761789726375 ,eval loss: 158.77665170450192\n",
      "epoch: 1 / 1 ,samples: 37 / 120 ,train loss: 164.91722726673692 ,eval loss: 149.26036101318223\n",
      "epoch: 1 / 1 ,samples: 38 / 120 ,train loss: 154.08437475131961 ,eval loss: 139.76767558665097\n",
      "epoch: 1 / 1 ,samples: 39 / 120 ,train loss: 143.8890967396074 ,eval loss: 130.8343368901387\n",
      "epoch: 1 / 1 ,samples: 40 / 120 ,train loss: 132.60651489715613 ,eval loss: 120.94947293213396\n",
      "epoch: 1 / 1 ,samples: 41 / 120 ,train loss: 122.05404130856687 ,eval loss: 111.70494507865556\n",
      "epoch: 1 / 1 ,samples: 42 / 120 ,train loss: 112.12358484286929 ,eval loss: 103.00579214468047\n",
      "epoch: 1 / 1 ,samples: 43 / 120 ,train loss: 102.89744660707402 ,eval loss: 94.92394460267941\n",
      "epoch: 1 / 1 ,samples: 44 / 120 ,train loss: 91.42133543792461 ,eval loss: 84.87225024380368\n",
      "epoch: 1 / 1 ,samples: 45 / 120 ,train loss: 78.13817406765556 ,eval loss: 73.23715667480447\n",
      "epoch: 1 / 1 ,samples: 46 / 120 ,train loss: 65.70062355560859 ,eval loss: 62.33800496293836\n",
      "epoch: 1 / 1 ,samples: 47 / 120 ,train loss: 54.02535849002556 ,eval loss: 52.094392295236844\n",
      "epoch: 1 / 1 ,samples: 48 / 120 ,train loss: 41.683807766260934 ,eval loss: 41.22614231474926\n",
      "epoch: 1 / 1 ,samples: 49 / 120 ,train loss: 30.433511697626553 ,eval loss: 31.185882623013303\n",
      "epoch: 1 / 1 ,samples: 50 / 120 ,train loss: 21.514229448428743 ,eval loss: 22.771017028293713\n",
      "epoch: 1 / 1 ,samples: 51 / 120 ,train loss: 17.16571530025454 ,eval loss: 17.13932905161945\n",
      "epoch: 1 / 1 ,samples: 52 / 120 ,train loss: 19.682964325018517 ,eval loss: 16.627893626619425\n",
      "epoch: 1 / 1 ,samples: 53 / 120 ,train loss: 26.17980787137188 ,eval loss: 20.579384922979518\n",
      "epoch: 1 / 1 ,samples: 54 / 120 ,train loss: 33.98060706754525 ,eval loss: 26.580680020029057\n",
      "epoch: 1 / 1 ,samples: 55 / 120 ,train loss: 40.718522653083056 ,eval loss: 32.11709555436113\n",
      "epoch: 1 / 1 ,samples: 56 / 120 ,train loss: 47.1280969103238 ,eval loss: 37.51589197307846\n",
      "epoch: 1 / 1 ,samples: 57 / 120 ,train loss: 53.23655823745947 ,eval loss: 42.72628701847929\n",
      "epoch: 1 / 1 ,samples: 58 / 120 ,train loss: 57.91439837653235 ,eval loss: 46.7435156480082\n",
      "epoch: 1 / 1 ,samples: 59 / 120 ,train loss: 62.2916257861008 ,eval loss: 50.517296176954176\n",
      "epoch: 1 / 1 ,samples: 60 / 120 ,train loss: 63.37465187759853 ,eval loss: 51.453547885856835\n",
      "epoch: 1 / 1 ,samples: 61 / 120 ,train loss: 62.79759163667555 ,eval loss: 50.95608888525005\n",
      "epoch: 1 / 1 ,samples: 62 / 120 ,train loss: 62.14927336845161 ,eval loss: 50.397184853260356\n",
      "epoch: 1 / 1 ,samples: 63 / 120 ,train loss: 58.68121179556597 ,eval loss: 47.407939684556275\n",
      "epoch: 1 / 1 ,samples: 64 / 120 ,train loss: 54.521834742756766 ,eval loss: 43.83360698214427\n",
      "epoch: 1 / 1 ,samples: 65 / 120 ,train loss: 50.152024574195885 ,eval loss: 40.09547496724038\n",
      "epoch: 1 / 1 ,samples: 66 / 120 ,train loss: 44.39992720800713 ,eval loss: 35.213398197916526\n",
      "epoch: 1 / 1 ,samples: 67 / 120 ,train loss: 39.333586356144004 ,eval loss: 30.969734556859894\n",
      "epoch: 1 / 1 ,samples: 68 / 120 ,train loss: 34.594628478215704 ,eval loss: 27.079303861985274\n",
      "epoch: 1 / 1 ,samples: 69 / 120 ,train loss: 30.59771443493049 ,eval loss: 23.897471633271614\n",
      "epoch: 1 / 1 ,samples: 70 / 120 ,train loss: 27.012269759755636 ,eval loss: 21.175346759436962\n",
      "epoch: 1 / 1 ,samples: 71 / 120 ,train loss: 24.206812085666503 ,eval loss: 19.193485550720503\n",
      "epoch: 1 / 1 ,samples: 72 / 120 ,train loss: 21.92448958652032 ,eval loss: 17.744576448472227\n",
      "epoch: 1 / 1 ,samples: 73 / 120 ,train loss: 20.28332003228646 ,eval loss: 16.855437337583528\n",
      "epoch: 1 / 1 ,samples: 74 / 120 ,train loss: 19.048206816991755 ,eval loss: 16.332043336716456\n",
      "epoch: 1 / 1 ,samples: 75 / 120 ,train loss: 18.153218019182912 ,eval loss: 16.097736138464715\n",
      "epoch: 1 / 1 ,samples: 76 / 120 ,train loss: 17.62019020726425 ,eval loss: 16.078084905499313\n",
      "epoch: 1 / 1 ,samples: 77 / 120 ,train loss: 17.276138511016793 ,eval loss: 16.184496145420404\n",
      "epoch: 1 / 1 ,samples: 78 / 120 ,train loss: 17.095845317846123 ,eval loss: 16.35939284722499\n",
      "epoch: 1 / 1 ,samples: 79 / 120 ,train loss: 17.01710369315616 ,eval loss: 16.582774084657874\n",
      "epoch: 1 / 1 ,samples: 80 / 120 ,train loss: 17.010142091853233 ,eval loss: 16.78141646069649\n",
      "epoch: 1 / 1 ,samples: 81 / 120 ,train loss: 17.039469465368413 ,eval loss: 16.983741883163493\n",
      "epoch: 1 / 1 ,samples: 82 / 120 ,train loss: 17.09361648072788 ,eval loss: 17.183288214670156\n",
      "epoch: 1 / 1 ,samples: 83 / 120 ,train loss: 17.103607965717508 ,eval loss: 17.215825153711435\n",
      "epoch: 1 / 1 ,samples: 84 / 120 ,train loss: 17.054551444977335 ,eval loss: 17.058868161589743\n",
      "epoch: 1 / 1 ,samples: 85 / 120 ,train loss: 17.002073634940558 ,eval loss: 16.763590578211996\n",
      "epoch: 1 / 1 ,samples: 86 / 120 ,train loss: 17.0432834771113 ,eval loss: 16.429314526411407\n",
      "epoch: 1 / 1 ,samples: 87 / 120 ,train loss: 17.20566174022564 ,eval loss: 16.206362628856045\n",
      "epoch: 1 / 1 ,samples: 88 / 120 ,train loss: 17.454192113839586 ,eval loss: 16.086808426814954\n",
      "epoch: 1 / 1 ,samples: 89 / 120 ,train loss: 17.810801283058517 ,eval loss: 16.047768615374864\n",
      "epoch: 1 / 1 ,samples: 90 / 120 ,train loss: 18.352418415549227 ,eval loss: 16.11499606954419\n",
      "epoch: 1 / 1 ,samples: 91 / 120 ,train loss: 18.949218549079465 ,eval loss: 16.281230192264882\n",
      "epoch: 1 / 1 ,samples: 92 / 120 ,train loss: 19.745142441168305 ,eval loss: 16.591784659561966\n",
      "epoch: 1 / 1 ,samples: 93 / 120 ,train loss: 20.56392873099257 ,eval loss: 16.981097554999526\n",
      "epoch: 1 / 1 ,samples: 94 / 120 ,train loss: 21.492873758070026 ,eval loss: 17.483056184086117\n",
      "epoch: 1 / 1 ,samples: 95 / 120 ,train loss: 22.638913137139614 ,eval loss: 18.166733085093927\n",
      "epoch: 1 / 1 ,samples: 96 / 120 ,train loss: 23.784450245546704 ,eval loss: 18.903240572621787\n",
      "epoch: 1 / 1 ,samples: 97 / 120 ,train loss: 25.096632758570667 ,eval loss: 19.79634156759791\n",
      "epoch: 1 / 1 ,samples: 98 / 120 ,train loss: 26.379321474881408 ,eval loss: 20.708880523495196\n",
      "epoch: 1 / 1 ,samples: 99 / 120 ,train loss: 27.730190859825026 ,eval loss: 21.703041579812098\n",
      "epoch: 1 / 1 ,samples: 100 / 120 ,train loss: 29.024156936889867 ,eval loss: 22.680837567070597\n",
      "epoch: 1 / 1 ,samples: 101 / 120 ,train loss: 30.446320541768557 ,eval loss: 23.778754692244608\n",
      "epoch: 1 / 1 ,samples: 102 / 120 ,train loss: 31.90272032223926 ,eval loss: 24.923654288406865\n",
      "epoch: 1 / 1 ,samples: 103 / 120 ,train loss: 33.25186049429429 ,eval loss: 25.999494771611396\n",
      "epoch: 1 / 1 ,samples: 104 / 120 ,train loss: 34.67854715054938 ,eval loss: 27.150439790282746\n",
      "epoch: 1 / 1 ,samples: 105 / 120 ,train loss: 34.645435697943064 ,eval loss: 27.123685246246136\n",
      "epoch: 1 / 1 ,samples: 106 / 120 ,train loss: 34.68994743526686 ,eval loss: 27.159874803596903\n",
      "epoch: 1 / 1 ,samples: 107 / 120 ,train loss: 34.81225420024342 ,eval loss: 27.259213281522232\n",
      "epoch: 1 / 1 ,samples: 108 / 120 ,train loss: 34.93032281889466 ,eval loss: 27.355191146766472\n",
      "epoch: 1 / 1 ,samples: 109 / 120 ,train loss: 35.214471007739654 ,eval loss: 27.58635316409818\n",
      "epoch: 1 / 1 ,samples: 110 / 120 ,train loss: 35.65887123004236 ,eval loss: 27.94868223998747\n",
      "epoch: 1 / 1 ,samples: 111 / 120 ,train loss: 35.95282974874901 ,eval loss: 28.188932498818\n",
      "epoch: 1 / 1 ,samples: 112 / 120 ,train loss: 36.40533677831546 ,eval loss: 28.559526013999065\n",
      "epoch: 1 / 1 ,samples: 113 / 120 ,train loss: 36.878308753734025 ,eval loss: 28.947889054255956\n",
      "epoch: 1 / 1 ,samples: 114 / 120 ,train loss: 37.38011945871737 ,eval loss: 29.360998690623084\n",
      "epoch: 1 / 1 ,samples: 115 / 120 ,train loss: 37.86842339837046 ,eval loss: 29.763993967202698\n",
      "epoch: 1 / 1 ,samples: 116 / 120 ,train loss: 38.40383736296162 ,eval loss: 30.20691691837426\n",
      "epoch: 1 / 1 ,samples: 117 / 120 ,train loss: 38.93430977980122 ,eval loss: 30.64680085481013\n",
      "epoch: 1 / 1 ,samples: 118 / 120 ,train loss: 39.50290701669599 ,eval loss: 31.119384447400858\n",
      "epoch: 1 / 1 ,samples: 119 / 120 ,train loss: 39.83255578849347 ,eval loss: 31.39391286683079\n",
      "epoch: 1 / 1 ,samples: 120 / 120 ,train loss: 40.15889993779814 ,eval loss: 31.66603426567604\n"
     ]
    }
   ],
   "source": [
    "#训练模型\n",
    "model = FM()\n",
    "train_losses,eval_losses = model.fit(X_train, y_train, eval_set=(X_test,y_test))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x2dac366ed30>]"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "#查看拟合效果\n",
    "plt.scatter(data[:, 0], target)\n",
    "plt.plot(data[:, 0], model.predict(data), color='r')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.legend.Legend at 0x2dadc90a8d0>"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "#查看loss\n",
    "plt.plot(range(0,len(train_losses)),train_losses,label='train loss')\n",
    "plt.plot(range(0,len(eval_losses)),eval_losses,label='eval loss')\n",
    "plt.legend()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
